﻿/* 
 * Rug.Cmd part of Rugland Console Framework
 * 
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
 * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * Copyright (C) 2008 Phill Tew. All rights reserved.
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using Rug.Cmd;

namespace Rug.Cmd
{
	#region Bool Switch

	/// <summary>
	/// The simplest kind of switch it is undefined by default.
	/// </summary>
	public class BoolSwitch : BaseArgument
	{
		public bool Value = false;

		/// <summary>
		/// The simplest kind of switch it is undefined by default.
		/// </summary>
		/// <param name="shortHelp">Short help for the argument overview</param>
		/// <param name="help">Long help for the arguent usage</param>
		public BoolSwitch(string shortHelp, string help)
			: base(shortHelp, help)
		{

		}

		/// <summary>
		/// Resets the argument
		/// </summary>
		public override void Reset()
		{
			Value = false;
			base.Reset();
		}

		/// <summary>
		/// Sets the value of this switch. e.g. 'true'
		/// </summary>
		/// <param name="value">string value</param>
		/// <returns>true if the value was parsed</returns>
		public override bool SetValue(string value)
		{
			return bool.TryParse(value, out Value);
		}

		public override object ObjectValue
		{
			get { return Value; }
		}

		public override string ArgumentString()
		{
			return "";
		}

		public override bool Parse(ArgumentParser parser, string key, string[] values, ref int index)
		{
			SetValue(bool.TrueString);
			return false;
		}
	}

	#endregion 

	#region Plus / Minus Switch

	/// <summary>
	/// Plus minus switch, use PlusMinusSwitch.KeyPrefix as the the 'prefix' argument when adding to the ArgumentParser
	/// </summary>
	public class PlusMinusSwitch : BaseArgument
	{
		public static string KeyPrefix
		{
			get { return new string(ConsoleChars.GetMathsChar(ConsoleMathsChars.PlusMinus), 1); } 
		}

		public bool Value = false;
		private bool m_Default = false;

		/// <summary>
		/// Plus minus switch, use PlusMinusSwitch.KeyPrefix as the the 'prefix' argument when adding to the ArgumentParser
		/// </summary>
		/// <param name="shortHelp">Short help for the argument overview</param>
		/// <param name="help">Long help for the arguent usage</param>
		/// <param name="default">The default value</param>
		public PlusMinusSwitch(string shortHelp, string help, bool @default)
			: base(shortHelp, help)
		{
			Value = @default;
			m_Default = @default; 
		}

		/// <summary>
		/// Resets the argument
		/// </summary>
		public override void Reset()
		{
			Value = m_Default;
			base.Reset();
		}

		/// <summary>
		/// Sets the value of this switch. e.g. 'true'
		/// </summary>
		/// <param name="value">string value</param>
		/// <returns>true if the value was parsed</returns>
		public override bool SetValue(string value)
		{
			return bool.TryParse(value, out Value);
		}

		public override object ObjectValue
		{
			get { return Value; }
		}

		public override string ArgumentString()
		{
			return "";
		}

		public override bool Parse(ArgumentParser parser, string key, string[] values, ref int index)
		{
			SetValue(key.StartsWith("+").ToString());
			return false;
		}
	}

	#endregion

	#region Enum Switch

	/// <summary>
	/// Switch for the value of a enuration.
	/// </summary>
	public class EnumSwitch : BaseArgument
	{
		public object Value;

		// private string[] m_ValidNames; 
		private Type m_Enumeration;
		public Type Enumeration { get { return m_Enumeration; } }

		/// <summary>
		/// Switch for the value of a enuration.
		/// </summary>
		/// <param name="shortHelp">Short help for the argument overview</param>
		/// <param name="help">Long help for the arguent usage</param>
		/// <param name="enumeration">Type of the enumeration</param>
		public EnumSwitch(string shortHelp, string help, Type enumeration)
			: base(shortHelp, help)
		{
			this.m_Enumeration = enumeration;
		}

		public override string Help
		{
			get
			{
				StringBuilder sb = new StringBuilder();

				sb.Append(string.Format("{0} : ", Strings.EnumSwitch_Posible));

				bool first = true;

				foreach (string str in Enum.GetNames(m_Enumeration))
				{
					if (first)
					{
						sb.Append(str);
						first = false;
					}
					else
						sb.Append(", " + str);
				}

				sb.AppendLine();
				sb.AppendLine();
				sb.AppendLine(base.Help);

				return sb.ToString(); ;
			}
		}

		/// <summary>
		/// Resets the argument
		/// </summary>
		public override void Reset()
		{
			Value = null;
			base.Reset();
		}

		/// <summary>
		/// Sets the value of this switch. it will attempt to parse the enum from the enumeration type.
		/// </summary>
		/// <param name="value">string value</param>
		/// <returns>true if the value was parsed</returns>
		public override bool SetValue(string value)
		{
			foreach (string str in Enum.GetNames(m_Enumeration))
			{
				if (str.Equals(value, StringComparison.InvariantCultureIgnoreCase))
				{
					Value = Enum.Parse(m_Enumeration, str);
					return true;
				}
			}

			return false;
		}

		public override object ObjectValue
		{
			get { return Value; }
		}

		public override string ArgumentString()
		{
			return " " + m_Enumeration.Name;
		}

		public override bool Parse(ArgumentParser parser, string key, string[] values, ref int index)
		{
			if (index + 1 < values.Length)
			{
				if (!SetValue(values[index + 1]))
				{ 
					throw new Exception(string.Format(Strings.EnumSwitch_Unknown, values[index + 1]));
				}

				index++;

				return false;
			}
			else
			{ 
				throw new Exception(string.Format(Strings.ArgumentParser_InvalidArgument, key));
			}
		}
	}

	#endregion

	#region String Argument

	/// <summary>
	/// Collects the next imediate argument is uses as its value
	/// </summary>
	public class StringArgument : BaseArgument
	{
		public string Value;
		private string m_Name;

		/// <summary>
		/// If defined the next imediate argument is uses as its value
		/// </summary>
		/// <param name="name">symbol name for the string in the arguments list readout. e.g. /?</param>
		/// <param name="shortHelp">Short help for the argument overview</param>
		/// <param name="help">Long help for the arguent usage</param>
		public StringArgument(string name, string shortHelp, string help)
			: base(shortHelp, help)
		{
			m_Name = name;
		}

		/// <summary>
		/// Resets the argument
		/// </summary>
		public override void Reset()
		{
			Value = null;
			base.Reset();
		}

		/// <summary>
		/// Sets the string value of this switch
		/// </summary>
		/// <param name="value">string value</param>
		/// <returns>true if the value was parsed</returns>
		public override bool SetValue(string value)
		{
			Value = value;

			return true;
		}

		public override object ObjectValue
		{
			get { return Value; }

		}
		public override string ArgumentString()
		{
			return " " + m_Name;
		}

		public override bool Parse(ArgumentParser parser, string key, string[] values, ref int index)
		{
			if (index + 1 < values.Length)
			{
				SetValue(values[index + 1]);
				index++;
				
				return false;
			}
			else
				throw new Exception(string.Format(Strings.ArgumentParser_InvalidArgument, key));
		}
	}

	#endregion

	#region Optional String Argument

	/// <summary>
	/// If defined and the next imediate argument is not a switch then it is used as its value
	/// </summary>
	public class OptionalStringArgument : BaseArgument
	{
		public string Value;
		private string m_Name;

		/// <summary>
		/// If defined an the next imediate argument is not a switch then it is uses as its value
		/// </summary>
		/// <param name="name">symbol name for the string in the arguments list readout. e.g. /?</param>
		/// <param name="shortHelp">Short help for the argument overview</param>
		/// <param name="help">Long help for the arguent usage</param>
		public OptionalStringArgument(string name, string shortHelp, string help)
			: base(shortHelp, help)
		{
			m_Name = name;
		}

		/// <summary>
		/// Resets the argument
		/// </summary>
		public override void Reset()
		{
			Value = null;
			base.Reset();
		}

		/// <summary>
		/// Sets the string value of this switch
		/// </summary>
		/// <param name="value">string value</param>
		/// <returns>true if the value was parsed</returns>
		public override bool SetValue(string value)
		{
			Value = value;

			return true;
		}

		public override object ObjectValue
		{
			get { return Value; }

		}
		public override string ArgumentString()
		{
			return " <" + m_Name + ">";
		}

		public override bool Parse(ArgumentParser parser, string key, string[] values, ref int index)
		{
			if (index + 1 < values.Length)
			{
				#region Scan forward to see if thew next token is a arg switch

				string nextKey = values[index + 1];
				string nextLookupKey = nextKey;

				if (nextKey.StartsWith("-") || nextKey.StartsWith("+"))
					nextLookupKey = ConsoleChars.GetMathsChar(ConsoleMathsChars.PlusMinus) + key.Substring(1);

				ArgumentKey actualKey = null; 
				IArgumentValue nextV = null;

				if (parser.ContainsKey(nextKey))
				{
					nextV = parser.GetForKey(nextKey, out actualKey);
				}
				else if (!nextLookupKey.Equals(nextKey) && parser.ContainsKey(nextLookupKey))
				{
					nextV = parser.GetForKey(nextLookupKey, out actualKey);
				}

				#endregion

				#region If the next token is not a arg switch then use its value

				if (actualKey == null || nextV == null)
				{
					SetValue(values[index + 1]);
					index++;
				}

				#endregion
			}

			return false; 
		}
	}

	#endregion

	#region Csv Argument

	/// <summary>
	/// Turns a CSV Argument string into a list of strings
	/// </summary>
	public class CsvArgument : BaseArgument
	{
		public List<string> Value = new List<string>();
		private string m_Name;

		/// <summary>
		/// Turns a CSV Argument string into a list of strings
		/// </summary>
		/// <param name="name">symbol name for the string in the arguments list readout. e.g. /?</param>
		/// <param name="shortHelp">Short help for the argument overview</param>
		/// <param name="help">Long help for the arguent usage</param>
		public CsvArgument(string name, string shortHelp, string help)
			: base(shortHelp, help)
		{
			m_Name = name;
		}

		/// <summary>
		/// Resets the argument
		/// </summary>
		public override void Reset()
		{
			Value.Clear();
			base.Reset();
		}

		/// <summary>
		/// Sets the value of this switch. will split the supplyed string by the ',' char and put the result in a list
		/// </summary>
		/// <param name="value">CSV string value</param>
		/// <returns>true if the value was parsed</returns>
		public override bool SetValue(string strValue)
		{
			Value.AddRange(strValue.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries));

			return true;
		}

		public override object ObjectValue
		{
			get { return Value; }

		}
		public override string ArgumentString()
		{
			return " " + m_Name;
		}

		public override bool Parse(ArgumentParser parser, string key, string[] values, ref int index)
		{
			if (index + 1 < values.Length)
			{
				SetValue(values[index + 1]);
				index++;

				return true;
			}
			else
				throw new Exception(string.Format(Strings.ArgumentParser_InvalidArgument, key));
		}
	}

	#endregion

	#region String List Argument

	/// <summary>
	/// Once defiend will collect strings untill escaped by another switch
	/// </summary>
	public class StringListArgument : BaseArgument
	{
		/// <summary>
		/// List of al the strings gathered by this StringListArgument
		/// </summary>
		public List<string> Value = new List<string>();
		private string m_Name;

		/// <summary>
		/// Once defiend will collect strings untill escaped by another switch
		/// </summary>
		/// <param name="name">symbol name for the string in the arguments list readout. e.g. /?</param>
		/// <param name="shortHelp">Short help for the argument overview</param>
		/// <param name="help">Long help for the arguent usage</param>
		public StringListArgument(string name, string shortHelp, string help)
			: base(shortHelp, help)
		{
			m_Name = name;
		}

		/// <summary>
		/// Resets the argument
		/// </summary>
		public override void Reset()
		{
			Value.Clear();
			base.Reset();
		}

		/// <summary>
		/// Adds the string to the list
		/// </summary>
		/// <param name="value">string to add</param>
		/// <returns>true</returns>
		public override bool SetValue(string value)
		{
			Value.Add(value);

			return true;
		}

		public override object ObjectValue
		{
			get { return Value; }

		}
		public override string ArgumentString()
		{
			return " " + m_Name;
		}

		public override bool Parse(ArgumentParser parser, string key, string[] values, ref int index)
		{
			if (index + 1 < values.Length)
			{
				return true;
			}
			else
				throw new Exception(string.Format(Strings.ArgumentParser_InvalidArgument, key));

		}
	}

	#endregion
}
